/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "secure_element_ffi.h"
#include "loghelper.h"
#include "secure_element_channel.h"
#include "secure_element_common.h"
#include "secure_element_reader.h"
#include "secure_element_seservice.h"
#include "secure_element_session.h"
#include "session.h"

using namespace OHOS::FFI;

namespace OHOS {
namespace SE {
namespace OMAPI {
extern "C" {
int64_t FfiSECreateService(int32_t *errorCode)
{
    auto seService = FFIData::Create<SEServiceImpl>();
    if (seService == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("FfiSECreateService failed");
        return CJ_INVALID_VALUE_64;
    }
    auto ret = seService->CreateService();
    if (ret != SUCCESS_CODE) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("CreateService failed");
        return CJ_INVALID_VALUE_64;
    }
    *errorCode = SUCCESS_CODE;
    return seService->GetID();
}

CArrI64 FfiSESESGetReaders(int64_t id, int32_t *errorCode)
{
    auto seService = FFIData::GetData<SEServiceImpl>(id);
    if (seService == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEServiceImpl instance failed");
        return CArrI64();
    }
    std::vector<std::shared_ptr<Reader>> readers = seService->GetReaders(errorCode);
    if (readers.empty()) {
        return CArrI64();
    }
    int64_t size = static_cast<int64_t>(readers.size());
    CArrI64 arr;
    int32_t mallocSize = static_cast<int32_t>(sizeof(SEReaderImpl)) * static_cast<int32_t>(size);
    if (mallocSize <= 0 || mallocSize > static_cast<int32_t>(sizeof(SEReaderImpl) * MAX_MEM_MALLOC_SIZE)) {
        return CArrI64();
    }
    auto head = static_cast<int64_t *>(malloc(mallocSize));
    arr.head = head;
    arr.size = size;
    if (arr.head == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        free(arr.head);
        return CArrI64();
    }
    for (int64_t i = 0; i < size; i++) {
        auto rd = FFIData::Create<SEReaderImpl>();
        if (rd == nullptr) {
            free(arr.head);
            return CArrI64();
        }
        rd->SetReader(readers[i]);
        head[i] = rd->GetID();
    }
    return arr;
}

bool FfiSESESIsConnected(int64_t id, int32_t *errorCode)
{
    auto seService = FFIData::GetData<SEServiceImpl>(id);
    if (seService == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEServiceImpl instance failed");
        return false;
    }
    return seService->IsConnected(errorCode);
}

void FfiSESESShutdown(int64_t id, int32_t *errorCode)
{
    auto seService = FFIData::GetData<SEServiceImpl>(id);
    if (seService == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEServiceImpl instance failed");
        return;
    }
    auto res = seService->Shutdown();
    if (res != NATIVE_SUCCESS) {
        ErrorLog("FfiSESESShutdown failed");
        *errorCode = CJ_UNSUPPORTED_ERR;
    }
}

char *FfiSESESGetVersion(int64_t id, int32_t *errorCode)
{
    auto seService = FFIData::GetData<SEServiceImpl>(id);
    if (seService == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEServiceImpl instance failed");
        return nullptr;
    }
    return MallocCString(seService->GetVersion(errorCode));
}

char *FfiSEReaderGetName(int64_t id, int32_t *errorCode)
{
    auto seReader = FFIData::GetData<SEReaderImpl>(id);
    if (seReader == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEReaderImpl instance failed");
        return nullptr;
    }
    return MallocCString(seReader->GetName(errorCode));
}

bool FfiSEReaderIsSecureElementPresent(int64_t id, int32_t *errorCode)
{
    auto seReader = FFIData::GetData<SEReaderImpl>(id);
    if (seReader == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEReaderImpl instance failed");
        return false;
    }
    return seReader->IsSecureElementPresent(errorCode);
}

int64_t FfiSEReaderOpenSession(int64_t id, int32_t *errorCode)
{
    auto seReader = FFIData::GetData<SEReaderImpl>(id);
    if (seReader == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEReaderImpl instance failed");
        return CJ_INVALID_VALUE_64;
    }
    return seReader->OpenSession(errorCode);
}

void FfiSEReaderCloseSessions(int64_t id, int32_t *errorCode)
{
    auto seReader = FFIData::GetData<SEReaderImpl>(id);
    if (seReader == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEReaderImpl instance failed");
        return;
    }
    seReader->CloseSessions(errorCode);
}

int64_t FfiSEChannelGetSession(int64_t id, int32_t *errorCode)
{
    auto chn = FFIData::GetData<SEChannelImpl>(id);
    if (chn == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEChannelImpl instance failed");
        return CJ_INVALID_VALUE_64;
    }
    return chn->GetSession(errorCode);
}

void FfiSEChannelClose(int64_t id, int32_t *errorCode)
{
    auto chn = FFIData::GetData<SEChannelImpl>(id);
    if (chn == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEChannelImpl instance failed");
        return;
    }
    chn->Close(errorCode);
}

bool FfiSEChannelIsClosed(int64_t id, int32_t *errorCode)
{
    bool res = false;
    auto chn = FFIData::GetData<SEChannelImpl>(id);
    if (chn == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEChannelImpl instance failed");
        return res;
    }
    return chn->IsClosed(errorCode);
}

bool FfiSEChannelIsBasicChannel(int64_t id, int32_t *errorCode)
{
    bool res = false;
    auto chn = FFIData::GetData<SEChannelImpl>(id);
    if (chn == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEChannelImpl instance failed");
        return res;
    }
    return chn->IsBasicChannel(errorCode);
}

CArrI32 FfiSEChannelGetSelectResponse(int64_t id, int32_t *errorCode)
{
    auto chn = FFIData::GetData<SEChannelImpl>(id);
    if (chn == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEChannelImpl instance failed");
        return CArrI32();
    }
    std::string str = chn->GetSelectResponse(errorCode);
    if (str.empty()) {
        *errorCode = SE_ILLEGAL_STATE_ERROR;
        ErrorLog("FfiSEChannelGetSelectResponse failed");
        return CArrI32();
    }
    return ConvertString2Arr(str);
}

CArrI32 FfiSEChannelTransmit(int64_t id, CArrI32 command, int32_t *errorCode)
{
    auto chn = FFIData::GetData<SEChannelImpl>(id);
    if (chn == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SEChannelImpl instance failed");
        return CArrI32();
    }
    std::string cmd = ConvertArr2String(command);
    if (cmd.empty()) {
        *errorCode = CJ_ILLEGAL_STATE_ERR;
        ErrorLog("ConvertArr2String failed");
        return CArrI32();
    }
    return ConvertString2Arr(chn->Transmit(cmd, errorCode));
}

int64_t FfiSESessionGetReader(int64_t id, int32_t *errorCode)
{
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return CJ_INVALID_VALUE_64;
    }
    return session->GetReader(errorCode);
}

CArrI32 FfiSESessionGetATR(int64_t id, int32_t *errorCode)
{
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return CArrI32();
    }
    std::string atr = session->GetATR(errorCode);
    if (atr.empty()) {
        *errorCode = SE_ILLEGAL_STATE_ERROR;
        ErrorLog("FfiSESessionGetATR failed");
        return CArrI32();
    }
    return ConvertString2Arr(atr);
}

int64_t FfiSESessionOpenBasicChannel(int64_t id, CArrI32 aid, int32_t *errorCode)
{
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return CJ_INVALID_VALUE_64;
    }
    std::string aidStr = ConvertArr2String(aid);
    if (aidStr.empty()) {
        *errorCode = CJ_ILLEGAL_STATE_ERR;
        ErrorLog("ConvertArr2String failed");
        return CJ_INVALID_VALUE_64;
    }
    return session->OpenBasicChannel(aidStr, errorCode);
}

int64_t FfiSESessionOpenBasicChannelWithP2(int64_t id, CArrI32 aid, int32_t p2, int32_t *errorCode)
{
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return CJ_INVALID_VALUE_64;
    }
    std::string aidStr = ConvertArr2String(aid);
    if (aidStr.empty()) {
        *errorCode = CJ_ILLEGAL_STATE_ERR;
        ErrorLog("ConvertArr2String failed");
        return CJ_INVALID_VALUE_64;
    }
    return session->OpenBasicChannel(aidStr, p2, errorCode);
}

int64_t FfiSESessionOpenLogicalChannel(int64_t id, CArrI32 aid, int32_t *errorCode)
{
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return CJ_INVALID_VALUE_64;
    }
    std::string aidStr = ConvertArr2String(aid);
    if (aidStr.empty()) {
        *errorCode = CJ_ILLEGAL_STATE_ERR;
        ErrorLog("ConvertArr2String failed");
        return CJ_INVALID_VALUE_64;
    }
    return session->OpenLogicalChannel(aidStr, errorCode);
}

int64_t FfiSESessionOpenLogicalChannelWithP2(int64_t id, CArrI32 aid, int32_t p2, int32_t *errorCode)
{
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return CJ_INVALID_VALUE_64;
    }
    std::string aidStr = ConvertArr2String(aid);
    if (aidStr.empty()) {
        *errorCode = CJ_ILLEGAL_STATE_ERR;
        ErrorLog("ConvertArr2String failed");
        return CJ_INVALID_VALUE_64;
    }
    return session->OpenLogicalChannel(aidStr, p2, errorCode);
}

void FfiSESessionClose(int64_t id, int32_t *errorCode)
{
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return;
    }
    session->Close(errorCode);
}

bool FfiSESessionIsClosed(int64_t id, int32_t *errorCode)
{
    bool res = false;
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return res;
    }
    return session->IsClosed(errorCode);
}

void FfiSESessionCloseChannels(int64_t id, int32_t *errorCode)
{
    auto session = FFIData::GetData<SESessionImpl>(id);
    if (session == nullptr) {
        *errorCode = CJ_UNSUPPORTED_ERR;
        ErrorLog("Get SESessionImpl instance failed");
        return;
    }
    session->CloseChannels(errorCode);
}
}
}  // namespace OMAPI
}  // namespace SE
}  // namespace OHOS